/**
    @brief Module Serializer, fichier source

    @author Adrien Plagnol
    @file serializer.cpp
    @version 1.0
    @date 2008/11/01

*/

#include <exception>
#include "serializer.h"

using namespace std;

Serializer::Serializer()
{
    fileStream_out = new ofstream();
    fileStream_in = new ifstream();
    setFileName("default"+_extention());
}

Serializer::Serializer(const string& fname)
{
    fileStream_out = new ofstream();
    fileStream_in = new ifstream();
    setFileName(fname+_extention());
}

Serializer::Serializer(const UFile& uf)
{
    fileStream_out = new ofstream();
    fileStream_in = new ifstream();
    setFileName(uf._owner()._nickname() + _extention());
}

Serializer::Serializer(const User& usr)
{
    fileStream_out = new ofstream();
    fileStream_in = new ifstream();
    setFileName(usr._nickname()+_extention());
}

Serializer::~Serializer(void)
{
    if (fileStream_out != NULL)
        delete fileStream_out;

    if (fileStream_in != NULL)
        delete fileStream_in;
}

/**
    @brief Ouvre le fichier de sauvegarde en lecture ou en ecriture

    @param mode mode d'ouverture ( w pour ecriture, r pour lecture )
    @return none

    Exemple :
    @code
    myserializer.open('w');
    @endcode
*/
void Serializer::open(char mode)
{
    if (!isOpen())
    {
        if (mode=='w')
        {
            fileStream_out->open(_fileName().c_str(), ios::binary);
        }
        if (mode=='r')
        {
            fileStream_in->open(_fileName().c_str(), ios::binary);
        }

        opened = true;
    }
}

/**
    @brief Ferme le flux de fichier

    @return none

    Exemple :
    @code
    myserializer.close();
    @endcode
*/
void Serializer::close(void)
{
    if (fileStream_out->is_open())
    {
        fileStream_out->close();
        opened = false;
    }
    if (fileStream_in->is_open())
    {
        fileStream_in->close();
        opened = false;
    }
}

/**
    @brief Sauvegarde un objet FileTree
    @param [in] ft Objet à sauvegarder
    @return none

    Exemple :
    @code
    myserializer.saveFileTree( myFileTree );
    @endcode
    @warning Le fichier doit être ouvert avec open() avant d'appeller cette méthode.
*/
void Serializer::saveFileTree(const FileTree& ft)
{
    int nHead=0; // nombre de noeuds à la racine
    int nChildren=0;

    file_tree tr = ft._localFiles();
    file_tree::pre_order_iterator end = tr.end();
    file_tree::pre_order_iterator it=tr.begin();
    nHead = tr.number_of_siblings(it)+1;

    open('w');
    fileStream_out->write((char*)&nHead, sizeof(int));
    while (it!=end)
    {
        nChildren = tr.number_of_children(it);
        fileStream_out->write((char*)&nChildren, sizeof(int));
        saveUFile(*it);

        ++it;
    }
}

/**
    @brief Charge un objet file_tree

    Charge l'objet et le place de le membre privé newfiletree

    @param none
    @return none

    Exemple :
    @code
    mySerializer.loadFileTree();
    myFileTree.setLocalFiles( mySerializer._newFileTree() );
    @endcode

    @warning L'objet chargé est un file_tree (module tree), pas un FileTree
*/
void Serializer::loadFileTree()
{
    UFile* newnode = NULL;
    int nHead, nChildren, i, j;
    file_tree::iterator it, courrant;
    file_tree::sibling_iterator itHead;

    open('r');
    fileStream_in->read((char*)&nHead, sizeof(int));

    for (j=0; j<nHead; j++)
    {
        fileStream_in->read((char*)&nChildren, sizeof(int));
        newnode = loadUFile();
        it = newfiletree.begin();
        courrant = newfiletree.insert(it, *newnode);
        for (i=0; i<nChildren; i++)
        {
            loadFileTreeFrom(courrant);
        }
    }

    return;
}

void Serializer::loadFileTreeFrom(file_tree::iterator insertpoint)
{
    UFile* newnode = NULL;
    int nChildren, i;
    file_tree::iterator it, courrant;
    it = insertpoint;

    fileStream_in->read((char*)&nChildren, sizeof(int));
    newnode = loadUFile();

    courrant = newfiletree.append_child(it, *newnode);

    for (i=0; i<nChildren; i++)
    {
        loadFileTreeFrom(courrant);
    }

    return;
}

/**
    @brief Vérifie si le fichier de sauvegarde est ouvert correctement
    @param none
    @return bool

    Exemple :
    @code
    if( mySerializer.isOpen() )
    {
     ...
    }
    @endcode
*/
bool Serializer::isOpen()
{
    return opened;
}

/**
    @brief Sauvegarde un objet UFile
    @param [in] UFile Objet à sauvegarder
    @return none

    Exemple :
    @code
    mySerializer.saveUFile( myUFile );
    @endcode

    @warning Le fichier doit être ouvert avec open() avant d'appeller cette méthode.
*/
void Serializer::saveUFile(const UFile& node)
{
    open('w');
    saveString(node._name());
    saveString(node._path());
    saveString(node._type());
    saveUser(node._owner());
}

/**
    @brief Charge un objet UFile
    @param none
    @return UFile* pointeur sur UFile

    Exemple :
    @code
    UFile* myUFile = NULL;
    myUFile = mySerializer.loadUFile();
    myUFile.doThings();
    ...
    delete myUFile;
    @endcode
*/
UFile* Serializer::loadUFile(void)
{
    UFile* pufile;
    pufile = new UFile();
    if (pufile != NULL)
    {
        open('r');
        pufile->setName(loadString());
        pufile->setPath(loadString());
        pufile->setType(loadString());
        pufile->setOwner(loadUser());
    }
    else
    {
        cout << "erreur allocation mémoire" << endl;
        perror("memory allocation error");
    }

    return pufile;
}

/**
    @brief Sauvegarde un objet User
    @param [in] usr Objet à sauvegarder
    @return none

    Exemple :
    @code
    mySerializer.saveUser( myUser );
    @endcode
*/
void Serializer::saveUser(const User& usr)
{
    open('w');
    saveString(usr._nickname());
    saveString(usr._address());
}

/**
    @brief Charge un objet User
    @param none
    @return User* Pointeur sur User

    Exemple :
    @code
    User* myUser = NULL;
    myUser = mySerializer.loadUser();
    myUser.doThings();
    ...
    delete myUser;
    @endcode
*/
User* Serializer::loadUser(void)
{
    User* puser = NULL;
    puser = new User();
    if (puser != NULL)
    {
        open('r');
        puser->setNickname(loadString());
        puser->setAddress(loadString());
    }

    return puser;
}

void Serializer::saveGroup(const Group& group)
{
    user_group::iterator it, end;
    end = group._end();
    int nbUser = group._size();

    open('w');
    saveString(group._name());
    fileStream_out->write((char*)&nbUser, sizeof(int));
    for(it = group._firstUser() ; it != end ; it++)
    {
        saveUser(*it);
    }
}

Group* Serializer::loadGroup(void)
{
    Group* grp = new Group;
    User* usr = NULL;
    string groupname;

    int nbUser=0;
    int i;

    open('r');
    groupname = loadString();
    grp->setName(groupname);

    fileStream_in->read((char*)&nbUser, sizeof(int));
    for(i=0; i<nbUser; i++)
    {
        usr = loadUser();
        grp->addMember(*usr);
    }

    return grp;
}

/**
    @brief Sauvegarde une chaine de caractères.
    @param [in] content Chaine à sauvegarder
    @return none

    Exemple :
    @code
    mySerializer.saveString("machaine");
    @endcode
*/
void Serializer::saveString(const string& content)
{
    // Pour stocker la taille de la chaine
    open('w');

    int size=0;
    // la taille du tableau de char qu'on va stocker est la taille
    // de la chaine + 1 pour le 0 à la fin
    size=content.size()+1;
    fileStream_out->write((char*)&size, sizeof(int));
    fileStream_out->write(content.c_str(), size);
}

/**
    @brief Charge une chaine de caractères
    @param none
    @return string Chaine chargée

    Exemple :
    @code
    String myString;
    myString = mySerializer.loadString();
    @endcode
*/
string Serializer::loadString(void)
{
    open('r');

    char* namebuffer = NULL;
    // Pour stocker la taille de la chaine
    int size=0;
    // lire la taille de la prochaine chaine
    fileStream_in->read((char*)&size, sizeof(int));

    // lire la chaine
    namebuffer = new char[size];
    string stringbuffer;

    fileStream_in->read(namebuffer, size);
    stringbuffer = string(namebuffer);
    delete [] namebuffer;
    cout << "loaded string "+stringbuffer << endl;
    return stringbuffer;
}

/**
    @brief Renvoie l'extention du fichier de sauvegarde
    @param none
    @return string

    Exemple :
    @code
    cout << mySerializer._extention() ;
    @endcode
*/
string Serializer::_extention() const
{
    return "-files";
}

/**
    @brief Renvoie le nom du fichier de sauvegarde
    @param none
    @return string

    Exemple :
    @code
    cout << mySerializer._fileName();
    @endcode
*/
string Serializer::_fileName() const
{
    return fileName;
}

/**
    @brief Renvoie le file_tree chargé.
    @param none
    @return file_tree Objet lu par loadFileTree()

    Exemple :
    @code
    myFileTree.setLocalFiles( mySerializer._newFileTree() );
    @endcode
*/
file_tree Serializer::_newFileTree() const
{
    return newfiletree;
}

/**
    @brief Modifie le nom du fichier de sauvegarde
    @param name Nom du fichier
    @return none

    Exemple :
    @code
    mySerializer.setFileName( "fichier.sauv" );
    @endcode

    @warning A éxecuter avant open()
*/
void Serializer::setFileName(const string& name)
{
    fileName = name;
}
